import hmac
import json
import logging
import time
import urllib.parse

import requests
import requests.auth

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
from coin.exchange.util.rate_limit import RateLimiter

FTX_API_URL = "https://ftx.com/api/"


class FTXAuth(requests.auth.AuthBase):
  def __init__(self, key_file):
    self._key = AuthKey.from_file(key_file)

  def __call__(self, req: requests.PreparedRequest):
    assert req.method == 'GET', req.method
    timestamp = int(time.time() * 1000)
    signature_payload = f'{timestamp}{req.method}{req.path_url}'.encode()
    if req.body:
      signature_payload += req.body
    signature = hmac.new(self._key.api_secret.encode(), signature_payload, 'sha256').hexdigest()
    req.headers.update({
        'FTX-KEY': self._key.api_key,
        'FTX-TS': str(timestamp),
        'FTX-SIGN': signature,
    })
    if self._key._raw.get('account_type') != 'master':
      req.headers.update({'FTX-SUBACCOUNT': self._key.owner})
    return req


class FTXSyncQueryer(object):
  def __init__(self, key_file, *, api_host=None, timeout=10):
    self._timeout = timeout
    self._auth = FTXAuth(key_file)
    self._api_host = api_host or FTX_API_URL
    self._logger = logging.getLogger(__name__)
    self.rate_limit = RateLimiter(20, 1.0)

  def auth_request(self, method, path, params):
    assert method == 'GET', method
    url = urllib.parse.urljoin(self._api_host, path)
    response = None
    try:
      response = requests.request(method,
                                  url,
                                  params=params,
                                  auth=self._auth,
                                  timeout=self._timeout)
      content = response.json()
      return content
    except requests.exceptions.RequestException:
      self._logger.exception('RequestException: %s')
      raise
    except json.JSONDecodeError:
      self._logger.exception('Json decode: %s', response.content)
      raise
    except Exception as e:
      self._logger.exception('Unknown exception, %s', e)
      raise

  def _query_nocheck(self, method, path, params):
    req_timestamp = time.time()
    content = self.auth_request(method, path, params)
    res_timestamp = time.time()
    status = requests.codes.ok
    update = RestUpdate(req_timestamp, res_timestamp, content, status)
    return update

  def query(self, method, path, params=None, disable_rate_limit=False):
    if not disable_rate_limit:
      while not self.rate_limit.check():
        time.sleep(0.1)
    else:
      self.rate_limit.check()
    return self._query_nocheck(method, path, params)


class FTXNativePrivateClient(object):
  def __init__(
      self,
      *,
      key_file,
      api_host=None,
      timeout=20,
      use_async=False,
  ):
    api_host = api_host or FTX_API_URL
    if use_async:
      raise NotImplementedError()
    else:
      self._auth_queryer = FTXSyncQueryer(
          key_file,
          api_host=api_host,
          timeout=timeout,
      )

  def _query(self, method, path, params=None):
    return self._auth_queryer.query(method, path, params=params)

  def query_account_balance(self):
    method = 'GET'
    path = 'wallet/balances'
    update = self._query(method, path)
    return update
